module klang.编译.主机.程序主机_;
import 全局;
import std.conv: ·to = to;
import std.functional: ·fd = toDelegate;
import ··类型_ = klang.编译.类型.类型_;
import 时间 = std.datetime.systime·temp;
import klang.编译.编译.编译_: 创建源文件;
import klang.编译.类型.类型_: 文本范围_, 编译选项基类_, 操作已取消异常_, 取消令牌_, 输出结果_, 写文件回调_, 定制转换组_, 检查_, 模块解析缓存_, 缓存包括重定向_, 解析的模块包括查找失败位置_, 解析的模块名称_, 前端接口_, 编译主机接口_, 程序主机接口_;
import klang.编译.路径.路径_: 路径_, 转为路径, 获取正规化绝对路径包括根, 正规化路径, 有扩展名, 文件扩展名是, 转换到相对路径;
import klang.编译.类型.枚举值.枚举值_: 比较结果_;
import klang.编译.类型检查.类型检查_: 创建类型检查;
import klang.编译.核心.核心_: 整数_, 词典_, 比较器比较值, 比较文本区分大小写, 创建词典, 循环每个, 返回零值, 创建数组;
import 核心 = klang.编译.核心.核心_;
import klang.编译.扫描工具.扫描工具_: 获取位置的行和列, 计算行和列的位置, 获取行开始组, 跳过杂项;
import klang.编译.语法节点.节点_: 源文件_, 模块导入信息_;
import klang.编译.输出.输出_: emitFiles;
import klang.编译.转换.转换_: getTransformers;
import klang.编译.工具.工具_: 诊断收集_, 比较诊断, 比较诊断相等, 创建文件诊断消息, 创建编译诊断, 格式化消息;
import klang.编译.系统.系统_: 系统_, sys, 生成DJB2哈希;
import klang.编译.诊断集.诊断集_: 诊断集_;
import klang.编译.诊断.诊断_: 诊断_, 诊断消息_, 诊断类别_, 诊断消息_链_, 诊断包括位置_, 诊断相关信息_;
alias 获取源文件函数类型_ = 源文件_ delegate(dstring, 解析的模块名称_, void delegate(dstring), bool);
alias 写文件函数类型_ = void delegate(dstring, dstring, bool, void delegate(dstring), 源文件_[]);
alias 读文件函数类型_ = dstring delegate(dstring);
alias 创建目录函数类型_ = void delegate(dstring);
alias 获取当前目录函数类型_ = dstring delegate();
alias 目录或文件存在函数类型_ = bool delegate(dstring);
klang.编译.主机.程序主机_.程序主机_ _旧主机 = ··null!(klang.编译.主机.程序主机_.程序主机_);
检查_ _生产类型检查;
检查_ _无诊断检查;
klang.编译.主机.程序主机_.程序主机_ _当前主机;
klang.编译.主机.程序主机_.编译主机_ 当前编译主机;
klang.编译.主机.程序主机_.诊断缓存_!(诊断_) _文件的语义诊断缓存;
klang.编译.主机.程序主机_.诊断缓存_!(诊断包括位置_) _文件的声明诊断缓存;
诊断_[] _配置文件诊断;
诊断收集_ _文件诊断组;
诊断收集_ _程序诊断组;
编译选项基类_ _options;
dstring[] _rootNames;
词典_!(源文件_) filesByName;
词典_!(bool) sourceFilesFoundSearchingNodeModules;
词典_!(bool) modulesWithElidedImports;
词典_!(源文件_) filesByNameIgnoreCase;
int currentNodeModulesDepth = 0;
int maxNodeModuleJsDepth = 0;
词典_!(klang.编译.主机.程序主机_.引用文件_[]) refFileMap;
bool shouldCreateNewSourceFile = false;
源文件_[] processingDefaultLibFiles;
源文件_[] processingOtherFiles;
词典_!(klang.编译.主机.程序主机_.OutputFingerprint_) outputFingerprints;
dstring 隔开样式系列 = "\u001B[7m"d;
dstring 隔开系列 = " "d;
dstring 重置转义序列 = "\u001B[0m"d;
dstring 省略号 = "..."d;
dstring 半缩进 = "  "d;
dstring 缩进 = "    "d;
static this() {
    _文件的语义诊断缓存 = new klang.编译.主机.程序主机_.诊断缓存_!(诊断_)();
    _文件的声明诊断缓存 = new klang.编译.主机.程序主机_.诊断缓存_!(诊断包括位置_)();
    _文件诊断组 = new 诊断收集_();
    _程序诊断组 = new 诊断收集_();
    filesByName = 创建词典!(源文件_)();
    sourceFilesFoundSearchingNodeModules = 创建词典!(bool)();
    modulesWithElidedImports = 创建词典!(bool)();
    filesByNameIgnoreCase = 创建词典!(源文件_)();
    refFileMap = 创建词典!(klang.编译.主机.程序主机_.引用文件_[])();
    processingDefaultLibFiles = 创建数组!(源文件_)();
    processingOtherFiles = 创建数组!(源文件_)();
}
/** @internal */
enum 前景色转义序列_ : dstring {
    灰色 = "\u001B[90m"d,
    红色 = "\u001B[91m"d,
    黄色 = "\u001B[93m"d,
    蓝色 = "\u001B[94m"d,
    青色 = "\u001B[96m"d
}
class 诊断缓存_(T) {
    词典_!(T[]) 每个文件;
    诊断_[] 全部诊断;
    this() { }
}
class 编译主机_ : 编译主机接口_ {
    系统_ 系统;
    前端接口_ 前端;
    编译选项基类_ 选项;
    bool 设置父节点;
    private 获取源文件函数类型_ _getSourceFile;
    private 创建目录函数类型_ _创建目录;
    private 获取当前目录函数类型_ _获取当前目录;
    private 目录或文件存在函数类型_ _目录存在;
    private 目录或文件存在函数类型_ _文件存在;
    private 写文件回调_ _写文件;
    private 读文件函数类型_ _读文件;
    this() { }
    this(编译选项基类_ _选项组, bool _设置父节点, 前端接口_ _前端, 系统_ _系统 = sys) {
        this.系统 = _系统;
        this.选项 = _选项组;
        this.设置父节点 = _设置父节点;
        this.前端 = _前端;
        this._读文件 = cast(读文件函数类型_)(&this.系统.读文件);
        this._getSourceFile = ._获取编译主机获取源文件函数(this.前端, this.系统, this.设置父节点);
        this._写文件 = ._获取编译主机写文件函数(this.前端, this.系统, this.选项);
        this._获取当前目录 = cast(获取当前目录函数类型_)(&this.系统.获取当前目录);
        this._创建目录 = cast(创建目录函数类型_)(&this.系统.创建目录);
        this._目录存在 = cast(目录或文件存在函数类型_)(&this.系统.目录存在);
        this._文件存在 = cast(目录或文件存在函数类型_)(&this.系统.文件存在);
    }
    dstring getCanonicalFileName(dstring 文件名) {
        return 核心.创建获取正规文件名函数(this.系统.是使用区分大小写文件系统)(文件名);
    }
    bool useCaseSensitiveFileNames() {
        return this.系统.是使用区分大小写文件系统;
    }
    dstring 获取执行文件路径() {
        return this.系统.获取执行文件路径();
    }
    void 重置获取源文件函数(获取源文件函数类型_ 获取函数) {
        this._getSourceFile = cast(获取源文件函数类型_)(获取函数);
    }
    获取源文件函数类型_ 获取获取源文件函数() {
        return this._getSourceFile;
    }
    源文件_ getSourceFile(dstring fileName, 解析的模块名称_ 解析的 = ··null!(解析的模块名称_), void delegate(dstring) onError = ··null!(void delegate(dstring)), bool 应创建新文件 = false) {
        return this._getSourceFile(fileName, 解析的, cast(void delegate(dstring))(onError), 应创建新文件);
    }
    void 重置编译选项(编译选项基类_ 选项) {
        this.选项 = 选项;
    }
    编译选项基类_ 获取编译选项() {
        return this.选项;
    }
    前端接口_ 获取前端() {
        return this.前端;
    }
    void 重置获取当前目录函数(获取当前目录函数类型_ 获取当前目录) {
        this._获取当前目录 = cast(获取当前目录函数类型_)(获取当前目录);
    }
    获取当前目录函数类型_ 获取获取当前目录函数() {
        return this._获取当前目录;
    }
    dstring 获取当前目录() {
        return this._获取当前目录();
    }
    void 重置文件存在函数(目录或文件存在函数类型_ 文件存在) {
        this._文件存在 = cast(目录或文件存在函数类型_)(文件存在);
    }
    目录或文件存在函数类型_ 获取文件存在函数() {
        return this._文件存在;
    }
    bool 文件存在(dstring 文件名) {
        return this._文件存在(文件名);
    }
    void 重置目录存在函数(目录或文件存在函数类型_ 目录存在) {
        this._目录存在 = cast(目录或文件存在函数类型_)(目录存在);
    }
    目录或文件存在函数类型_ 获取目录存在函数() {
        return this._目录存在;
    }
    bool 目录存在(dstring 目录) {
        return this._目录存在(目录);
    }
    void 重置创建目录函数(创建目录函数类型_ 创建目录) {
        this._创建目录 = cast(创建目录函数类型_)(创建目录);
    }
    创建目录函数类型_ 获取创建目录函数() {
        return this._创建目录;
    }
    void 创建目录(dstring 目录名) {
        this._创建目录(目录名);
    }
    void 重置读文件函数(读文件函数类型_ 读文件) {
        this._读文件 = cast(读文件函数类型_)(读文件);
    }
    读文件函数类型_ 获取读文件函数() {
        return this._读文件;
    }
    dstring 读文件(dstring 文件名) {
        return this._读文件(文件名);
    }
    void 重置写文件函数(写文件回调_ 写文件) {
        this._写文件 = cast(写文件回调_)(写文件);
    }
    写文件回调_ 获取写文件函数() {
        return this._写文件;
    }
    void writeFile(dstring fileName, dstring data, void delegate(dstring) onError = ··null!(void delegate(dstring))) {
        this._写文件(fileName, data, false, cast(void delegate(dstring))(onError), ··null!(源文件_[]));
    }
    dstring[] 读目录(dstring 路径, dstring[] 扩展组 = [], dstring[] 排除组 = [], dstring[] 包含组 = [], int 深度 = 0) {
        return this.系统.读目录(路径, 扩展组, 排除组, 包含组, 深度);
    }
    模块解析缓存_ 创建模块解析缓存() {
        return new 模块解析缓存_(.创建缓存包括重定向!(词典_!(解析的模块包括查找失败位置_))(), .创建缓存包括重定向!(··类型_.每个模块名称缓存_)(), this.获取当前目录(), cast(核心.获取正规文件名函数_)(&this.getCanonicalFileName));
    }
    void 追踪(诊断消息_ 消息, dstring[] args) {
        args = args.dup;
        this.系统.写出(格式化消息(消息, args));
    }
}
class 程序主机_ : 程序主机接口_ {
    编译选项基类_ 编译选项;
    前端接口_ 前端;
    编译主机_ host;
    private 源文件_[] _files;
    private dstring[] _根文件组;
    private 获取源文件函数类型_ _getSourceFile;
    private 创建目录函数类型_ _创建目录;
    private 获取当前目录函数类型_ _获取当前目录;
    private 目录或文件存在函数类型_ _目录存在;
    private 目录或文件存在函数类型_ _文件存在;
    private 写文件回调_ _写文件;
    private 读文件函数类型_ _读文件;
    bool 是转换主机;
    this() { }
    this(前端接口_ 前端, 编译选项基类_ 编译选项, 源文件_[] 文件组) {
        this.是转换主机 = true;
        this.前端 = 前端;
        this.编译选项 = 编译选项;
        ._options = this.编译选项;
        this._根文件组 = 创建数组!(dstring)();
        this.host = new 编译主机_(this.编译选项, false, this.前端);
        this._getSourceFile = cast(获取源文件函数类型_)(·fd(&.getSourceFile));
        this._创建目录 = this.host.获取创建目录函数();
        this._获取当前目录 = this.host.获取获取当前目录函数();
        this._目录存在 = this.host.获取目录存在函数();
        this._文件存在 = this.host.获取文件存在函数();
        this._写文件 = this.host.获取写文件函数();
        this._读文件 = this.host.获取读文件函数();
        this._读文件 = this.host._读文件;
        .当前编译主机 = this.host;
        this._files = 文件组;
        .processingDefaultLibFiles = [];
        .processingOtherFiles = [];
    }
    this(创建主机选项_ 选项) {
        this.是转换主机 = false;
        this.前端 = 选项.前端;
        this.编译选项 = 选项.编译选项;
        ._rootNames = 选项.根文件组;
        this._根文件组 = ._rootNames;
        ._旧主机 = 选项.旧主机;
        ._配置文件诊断 = 选项.配置文件诊断;
        ._options = 选项.编译选项;
        if (选项.编译主机 !is ··null!(编译主机_)) {
            this.host = 选项.编译主机;
        }
        else {
            this.host = new 编译主机_(this.编译选项, false, this.前端);
        }
        this._getSourceFile = cast(获取源文件函数类型_)(·fd(&.getSourceFile));
        this._创建目录 = this.host.获取创建目录函数();
        this._获取当前目录 = this.host.获取获取当前目录函数();
        this._目录存在 = this.host.获取目录存在函数();
        this._文件存在 = this.host.获取文件存在函数();
        this._写文件 = this.host.获取写文件函数();
        this._读文件 = this.host.获取读文件函数();
        this._读文件 = this.host._读文件;
        .当前编译主机 = this.host;
        核心.循环每个!(dstring, void*)(._rootNames, (dstring v, size_t i) {
            ((dstring name, size_t _) { return .processRootFile(name, /*isDefaultLib*/ false); })(v, i);
            return null;
        });
        if (_rootNames.length > 0) {
            dstring[] 默认支持库 = this.前端.获取默认支持库文件名组();
            if (默认支持库.length > 0) {
                核心.循环每个!(dstring, void*)(默认支持库, (dstring v, size_t i) {
                    ((dstring name, size_t _) { return .processRootFile(name, /*isDefaultLib*/ true); })(v, i);
                    return null;
                });
            }
        }
        this._files = ··spread!(源文件_)(cast()(.processingDefaultLibFiles), cast()(.processingOtherFiles));
        .processingDefaultLibFiles = [];
        .processingOtherFiles = [];
    }
    dstring getCanonicalFileName(dstring 文件名) {
        return this.host.getCanonicalFileName(文件名);
    }
    bool useCaseSensitiveFileNames() {
        return this.host.useCaseSensitiveFileNames();
    }
    编译选项基类_ 获取编译选项() {
        return this.编译选项;
    }
    前端接口_ 获取前端() {
        return this.前端;
    }
    检查_ 获取类型检查() {
        return .获取类型检查();
    }
    dstring[] getRootFileNames() {
        return this._根文件组;
    }
    源文件_ getSourceFileByPath(dstring 路径) {
        return .getSourceFileByPath(new 路径_(路径));
    }
    源文件_ 获取源文件(dstring 文件名) {
        return this._getSourceFile(文件名, ··null!(解析的模块名称_), ··null!(void delegate(dstring)), ··null!(bool));
    }
    源文件_[] 获取源文件组() {
        return this._files;
    }
    诊断_[] 获取配置文件诊断组() {
        return ._配置文件诊断;
    }
    诊断_[] 获取语法诊断(源文件_ 源文件 = ··null!(源文件_), 取消令牌_ 取消令牌 = ··null!(取消令牌_)) {
        return .获取语法诊断(源文件, 取消令牌);
    }
    诊断_[] 获取语义诊断(源文件_ 源文件 = ··null!(源文件_), 取消令牌_ 取消令牌 = ··null!(取消令牌_)) {
        return .获取语义诊断(源文件, 取消令牌);
    }
    诊断_[] 获取选项诊断() {
        return .获取选项诊断();
    }
    诊断_[] 获取全局诊断组() {
        return .获取全局诊断组();
    }
    输出结果_ 输出(源文件_ 源文件 = ··null!(源文件_), 写文件回调_ 写文件回调 = ··null!(写文件回调_), 取消令牌_ 取消令牌 = ··null!(取消令牌_), 定制转换组_ transformers = ··null!(定制转换组_)) {
        写文件回调_ 写回调 = 写文件回调 ? cast(写文件回调_)(写文件回调) : this._写文件;
        return .运行包含取消令牌!(输出结果_)(() { return .emitWorker(this, 源文件, cast(写文件回调_)(写回调), 取消令牌, transformers); });
    }
    dstring 获取执行文件路径() {
        return this.host.获取执行文件路径();
    }
    dstring 获取当前目录() {
        return this.host.获取当前目录();
    }
    bool 文件存在(dstring 文件名) {
        return this.host.文件存在(文件名);
    }
    bool 目录存在(dstring 目录) {
        return this.host.目录存在(目录);
    }
    void 重置读文件函数(dstring delegate(dstring) 新函数) {
        this._读文件 = cast(读文件函数类型_)(新函数);
    }
    dstring 读文件(dstring 文件名) {
        return this._读文件(文件名);
    }
    模块解析缓存_ 创建模块解析缓存() {
        return this.host.创建模块解析缓存();
    }
    void 追踪(诊断消息_ 消息, dstring[] args) {
        args = args.dup;
        this.host.追踪(消息, args);
    }
    dstring[] 读目录(dstring 路径, dstring[] 扩展组 = [], dstring[] 排除组 = [], dstring[] 包含组 = [], int 深度 = 0) {
        return this.host.读目录(路径, 扩展组, 排除组, 包含组, 深度);
    }
    源文件_[][] 准备输出源文件(源文件_[] 源文件) {
        return this.host.获取前端().获取源文件输出准备函数()(源文件);
    }
}
class 引用文件_ : 文本范围_ {
    int index;
    源文件_ file;
    this() { }
    this(int 索引, 源文件_ 文件, long 开始 = -1, long 结束 = -1) {
        this.开始 = 开始;
        this.结束 = 结束;
        this.index = 索引;
        this.file = 文件;
    }
}
struct 主机环境_ {
    程序主机_ 旧主机;
    检查_ 生产类型检查;
    检查_ 无诊断检查;
    程序主机_ 当前主机;
    编译主机_ 当前编译主机;
    诊断缓存_!(诊断_) 文件的语义诊断缓存;
    诊断缓存_!(诊断包括位置_) 文件的声明诊断缓存;
    诊断_[] 配置文件诊断;
    诊断收集_ 文件诊断组;
    诊断收集_ 程序诊断组;
    编译选项基类_ options;
    dstring[] rootNames;
    词典_!(源文件_) filesByName;
    词典_!(bool) sourceFilesFoundSearchingNodeModules;
    词典_!(bool) modulesWithElidedImports;
    词典_!(源文件_) filesByNameIgnoreCase;
    int currentNodeModulesDepth;
    int maxNodeModuleJsDepth;
    词典_!(引用文件_[]) refFileMap;
    bool shouldCreateNewSourceFile;
    源文件_[] processingDefaultLibFiles;
    源文件_[] processingOtherFiles;
    词典_!(OutputFingerprint_) outputFingerprints;
    this(程序主机_ 旧主机, 检查_ 生产类型检查, 检查_ 无诊断检查, 程序主机_ 当前主机, 编译主机_ 当前编译主机, 诊断缓存_!(诊断_) 文件的语义诊断缓存, 诊断缓存_!(诊断包括位置_) 文件的声明诊断缓存, 诊断_[] 配置文件诊断, 诊断收集_ 文件诊断组, 诊断收集_ 程序诊断组, 编译选项基类_ options, dstring[] rootNames, 词典_!(源文件_) filesByName, 词典_!(bool) sourceFilesFoundSearchingNodeModules, 词典_!(bool) modulesWithElidedImports, 词典_!(源文件_) filesByNameIgnoreCase, int currentNodeModulesDepth, int maxNodeModuleJsDepth, 词典_!(引用文件_[]) refFileMap, bool shouldCreateNewSourceFile, 源文件_[] processingDefaultLibFiles, 源文件_[] processingOtherFiles, 词典_!(OutputFingerprint_) outputFingerprints) {
        this.旧主机 = 旧主机;
        this.生产类型检查 = 生产类型检查;
        this.无诊断检查 = 无诊断检查;
        this.当前主机 = 当前主机;
        this.当前编译主机 = 当前编译主机;
        this.文件的语义诊断缓存 = 文件的语义诊断缓存;
        this.文件的声明诊断缓存 = 文件的声明诊断缓存;
        this.配置文件诊断 = 配置文件诊断;
        this.文件诊断组 = 文件诊断组;
        this.程序诊断组 = 程序诊断组;
        this.options = options;
        this.rootNames = rootNames;
        this.filesByName = filesByName;
        this.sourceFilesFoundSearchingNodeModules = sourceFilesFoundSearchingNodeModules;
        this.modulesWithElidedImports = modulesWithElidedImports;
        this.filesByNameIgnoreCase = filesByNameIgnoreCase;
        this.currentNodeModulesDepth = currentNodeModulesDepth;
        this.maxNodeModuleJsDepth = maxNodeModuleJsDepth;
        this.refFileMap = refFileMap;
        this.shouldCreateNewSourceFile = shouldCreateNewSourceFile;
        this.processingDefaultLibFiles = processingDefaultLibFiles;
        this.processingOtherFiles = processingOtherFiles;
        this.outputFingerprints = outputFingerprints;
    }
}
struct OutputFingerprint_ {
    dstring 哈希;
    时间.SysTime 修改时间;
    this(dstring 哈希, 时间.SysTime 修改时间) {
        this.哈希 = 哈希;
        this.修改时间 = 修改时间;
    }
}
struct 创建主机选项_ {
    dstring[] 根文件组;
    编译选项基类_ 编译选项;
    前端接口_ 前端;
    编译主机_ 编译主机 = ··null!(编译主机_);
    程序主机_ 旧主机 = ··null!(程序主机_);
    诊断_[] 配置文件诊断 = ··null!(诊断_[]);
    this(前端接口_ 前端, 编译选项基类_ 编译选项, dstring[] 根文件组, 编译主机_ 编译主机 = ··null!(编译主机_), 程序主机_ 旧主机 = ··null!(程序主机_), 诊断_[] 配置文件诊断 = ··null!(诊断_[])) {
        this.前端 = 前端;
        this.编译选项 = 编译选项;
        this.根文件组 = 根文件组;
        this.旧主机 = 旧主机;
        this.配置文件诊断 = 配置文件诊断;
        this.编译主机 = 编译主机;
    }
}
interface 格式化诊断主机_ {
    dstring 获取当前目录();
    dstring 获取规范文件名(dstring 文件名);
    dstring 获取新行();
}
void 恢复主机环境(主机环境_ 环境) {
    ._旧主机 = 环境.旧主机;
    ._生产类型检查 = 环境.生产类型检查;
    ._无诊断检查 = 环境.无诊断检查;
    ._当前主机 = 环境.当前主机;
    .当前编译主机 = 环境.当前编译主机;
    ._文件的语义诊断缓存 = 环境.文件的语义诊断缓存;
    ._文件的声明诊断缓存 = 环境.文件的声明诊断缓存;
    ._配置文件诊断 = 环境.配置文件诊断;
    ._文件诊断组 = 环境.文件诊断组;
    ._程序诊断组 = 环境.程序诊断组;
    ._options = 环境.options;
    ._rootNames = 环境.rootNames;
    .filesByName = 环境.filesByName;
    .sourceFilesFoundSearchingNodeModules = 环境.sourceFilesFoundSearchingNodeModules;
    .modulesWithElidedImports = 环境.modulesWithElidedImports;
    .filesByNameIgnoreCase = 环境.filesByNameIgnoreCase;
    .currentNodeModulesDepth = 环境.currentNodeModulesDepth;
    .maxNodeModuleJsDepth = 环境.maxNodeModuleJsDepth;
    .refFileMap = 环境.refFileMap;
    .shouldCreateNewSourceFile = 环境.shouldCreateNewSourceFile;
    .processingDefaultLibFiles = 环境.processingDefaultLibFiles;
    .processingOtherFiles = 环境.processingOtherFiles;
    .outputFingerprints = 环境.outputFingerprints;
}
主机环境_ 重置主机环境() {
    主机环境_ 环境 = .主机环境_(._旧主机, ._生产类型检查, ._无诊断检查, ._当前主机, .当前编译主机, ._文件的语义诊断缓存, ._文件的声明诊断缓存, ._配置文件诊断, ._文件诊断组, ._程序诊断组, ._options, ._rootNames, .filesByName, .sourceFilesFoundSearchingNodeModules, .modulesWithElidedImports, .filesByNameIgnoreCase, .currentNodeModulesDepth, .maxNodeModuleJsDepth, .refFileMap, .shouldCreateNewSourceFile, .processingDefaultLibFiles, .processingOtherFiles, .outputFingerprints);
    ._旧主机 = ··null!(程序主机_);
    ._生产类型检查 = ··null!(检查_);
    ._无诊断检查 = ··null!(检查_);
    ._当前主机 = ··null!(程序主机_);
    .当前编译主机 = ··null!(编译主机_);
    ._文件的语义诊断缓存 = new 诊断缓存_!(诊断_)();
    ._文件的声明诊断缓存 = new 诊断缓存_!(诊断包括位置_)();
    ._配置文件诊断 = ··null!(诊断_[]);
    ._文件诊断组 = new 诊断收集_();
    ._程序诊断组 = new 诊断收集_();
    ._options = ··null!(编译选项基类_);
    ._rootNames = ··null!(dstring[]);
    .filesByName = 创建词典!(源文件_)();
    .sourceFilesFoundSearchingNodeModules = 创建词典!(bool)();
    .modulesWithElidedImports = 创建词典!(bool)();
    .filesByNameIgnoreCase = 创建词典!(源文件_)();
    .currentNodeModulesDepth = 0;
    .maxNodeModuleJsDepth = 0;
    .refFileMap = 创建词典!(引用文件_[])();
    .shouldCreateNewSourceFile = false;
    .processingDefaultLibFiles = 创建数组!(源文件_)();
    .processingOtherFiles = 创建数组!(源文件_)();
    .outputFingerprints = ··null!(词典_!(OutputFingerprint_));
    return 环境;
}
void 写文件如果有更新(dstring fileName, dstring data) {
    if ((.outputFingerprints is null) || (.outputFingerprints.原型 is ··null!(OutputFingerprint_[dstring]))) {
        .outputFingerprints = 核心.创建词典!(OutputFingerprint_)();
    }
    dstring 哈希 = 生成DJB2哈希(data);
    时间.SysTime 修改时间 = sys.获取修改时间(fileName);
    if (outputFingerprints.具有(fileName)) {
        OutputFingerprint_ fingerprint = outputFingerprints.获取(fileName);
        if (fingerprint.哈希 == 哈希 && fingerprint.修改时间 == 修改时间) {
            return;
        }
    }
    sys.写文件(fileName, data);
    修改时间 = sys.获取修改时间(fileName);
    outputFingerprints.p·设置(fileName, .OutputFingerprint_(哈希, 修改时间));
    return;
}
获取源文件函数类型_ _获取编译主机获取源文件函数(前端接口_ 前端, 系统_ 系统, bool 设置父节点) {
    源文件_ delegate(dstring, 解析的模块名称_, void delegate(dstring), bool) getSourceFile;
    源文件_ getSourceFile·func(dstring fileName, 解析的模块名称_ 解析的 = ··null!(解析的模块名称_), void delegate(dstring) onError = ··null!(void delegate(dstring)), bool 应创建新文件 = false) {
        dstring text;
        try {
            text = 系统.读文件(fileName);
        }
        catch(Throwable e) {
            if (onError !is ··null!(void delegate(dstring))) {
                onError(·to!(dstring)(e.msg));
            }
            text = ""d;
        }
        if (text !is ··null!(dstring)) {
            return 创建源文件(fileName, text, 前端, 设置父节点, 解析的);
        }
        return ··null!(源文件_);
    }
    getSourceFile = &getSourceFile·func;
    return cast(获取源文件函数类型_)(getSourceFile);
}
写文件回调_ _获取编译主机写文件函数(前端接口_ 前端, 系统_ 系统, 编译选项基类_ 选项) {
    void delegate(dstring, dstring, bool, void delegate(dstring), 源文件_[]) writeFile;
    void writeFile·func(dstring fileName, dstring data, bool writeByteOrderMark, void delegate(dstring) onError = ··null!(void delegate(dstring)), 源文件_[] sourceFiles = ··null!(源文件_[])) {
        try {
            if (前端.是监控设置(选项)) {
                .写文件如果有更新(fileName, data);
            }
            else {
                系统.写文件(fileName, data);
            }
        }
        catch(Throwable e) {
            if (onError !is ··null!(void delegate(dstring))) {
                onError(·to!(dstring)(e.msg));
            }
        }
    }
    writeFile = &writeFile·func;
    return cast(写文件回调_)(writeFile);
}
缓存包括重定向_!(T) 创建缓存包括重定向(T)() {
    return new 缓存包括重定向_!(T)(核心.创建词典!(T)());
}
程序主机_ 创建转换主机(前端接口_ 前端, 编译选项基类_ 编译选项, 源文件_[] 文件组) {
    ._当前主机 = new 程序主机_(前端, 编译选项, 文件组);
    return ._当前主机;
}
程序主机接口_ 创建主机(前端接口_ 前端, 编译选项基类_ 编译选项, dstring[] 根文件组, 诊断_[] 配置文件诊断 = ··null!(诊断_[])) {
    创建主机选项_ 选项 = .创建主机选项_(前端, 编译选项, 根文件组, ··null!(编译主机_), ··null!(程序主机_), 配置文件诊断);
    ._当前主机 = new 程序主机_(选项);
    return ._当前主机;
}
检查_ 获取生产类型检查(bool 报告错误 = true) {
    if (!报告错误) {
        return 创建类型检查(._当前主机, false);
    }
    if (._生产类型检查 !is ··null!(检查_)) {
        return ._生产类型检查;
    }
    ._生产类型检查 = 创建类型检查(._当前主机, /*报告错误*/ true);
    return ._生产类型检查;
}
void 设置生产类型检查为空值() {
    ._生产类型检查 = ··null!(检查_);
}
检查_ 获取类型检查() {
    if (._无诊断检查 !is ··null!(检查_)) {
        return ._无诊断检查;
    }
    ._无诊断检查 = 创建类型检查(._当前主机, /*报告错误*/ false);
    return ._无诊断检查;
}
T 运行包含取消令牌(T)(T delegate() 运行函数) {
    dstring f;
    try {
        return 运行函数();
    }
    catch(Throwable e) {
        操作已取消异常_ ···temp·0·_1 = (cast(操作已取消异常_)(e));
        if (···temp·0·_1 !is ··null!(操作已取消异常_)) {
            ._无诊断检查 = ··null!(检查_);
            ._生产类型检查 = ··null!(检查_);
        }
        f = ·to!(dstring)(e.file);
        throw e;
    }
}
前景色转义序列_ 获取诊断类别格式(诊断类别_ 诊断类别) {
    switch (诊断类别) {
        case 诊断类别_.Error: {
            return 前景色转义序列_.红色;
        }
        case 诊断类别_.Warning: {
            return 前景色转义序列_.黄色;
        }
        case 诊断类别_.Message: {
            return 前景色转义序列_.蓝色;
        }
        case 诊断类别_.Suggestion: {
            assert(false, "Should never get an Info 诊断 on the command line."c);
            return 前景色转义序列_.黄色;
        }
        default: break;
    }
    assert(false, "Should never get an Info 诊断 on the command line."c);
}
/** @internal */
dstring 格式化颜色和剩余(dstring 内容, dstring 格式化风格) {
    return 格式化风格 ~ 内容 ~ .重置转义序列;
}
dstring 补充左边(dstring s, long 长) {
    while(s.length < 长) {
        s = " "d ~ s;
    }
    return s;
}
dstring 格式化诊断组(诊断_[] 诊断组, 格式化诊断主机_ 主机) {
    dstring 输出文本 = ""d;
    foreach (诊断_ 诊断; 诊断组) {
        输出文本 ~= .格式化诊断(诊断, 主机);
    }
    return 输出文本;
}
/** @internal */
dstring 获取诊断类别名称(诊断_ d) {
    dstring name = ""d;
    switch (d.诊断类别) {
        case 诊断类别_.Warning: {
            return "警告"d;
        }
        case 诊断类别_.Error: {
            return "错误"d;
        }
        case 诊断类别_.Suggestion: {
            return "建议"d;
        }
        default: break;
    }
    return "消息"d;
}
dstring 格式化诊断(诊断_ 诊断, 格式化诊断主机_ 主机) {
    dstring 错误消息文本 = ``d ~ .获取诊断类别名称(诊断) ~ ` K`d ~ 诊断.代码.·to!(dstring)() ~ `: `d ~ .格式化诊断消息文本(诊断.消息文本, 主机.获取新行()) ~ ``d ~ 主机.获取新行() ~ ``d;
    if (诊断.文件 !is ··null!(Object)) {
        ··类型_.行和列_ 行和列 = 获取位置的行和列((cast(源文件_)(诊断.文件)), 诊断.起点); // TODO: GH#18217
        dstring 文件名 = (cast(源文件_)(诊断.文件)).文件名.原型;
        dstring 相对文件名 = 转换到相对路径(文件名, 主机.获取当前目录(), ( 文件名) { return 主机.获取规范文件名(文件名); });
        return ``d ~ 相对文件名 ~ `(`d ~ (行和列.行 + 1).·to!(dstring)() ~ `,`d ~ (行和列.列 + 1).·to!(dstring)() ~ `): `d ~ 错误消息文本;
    }
    return 错误消息文本;
}
dstring 格式化诊断消息文本(诊断消息_链_ 消息链 = ··null!(诊断消息_链_), dstring 新行 = ··null!(dstring), long 缩进 = 0) {
    if (消息链 is ··null!(诊断消息_链_)) {
        return ""d;
    }
    dstring 结果 = ""d;
    if (缩进 != 0) {
        结果 ~= 新行;
        {
            for (int i = 0; i < 缩进; ++i) {
                结果 ~= "  "d;
            }
        }
    }
    结果 ~= 消息链.消息文本;
    ++缩进;
    if (消息链.下个 !is ··null!(诊断消息_链_[])) {
        foreach (诊断消息_链_ 下个; 消息链.下个) {
            结果 ~= .格式化诊断消息文本(下个, 新行, 缩进);
        }
    }
    return 结果;
}
/** @internal */
dstring 格式化位置(源文件_ 文件, long 起点, 格式化诊断主机_ 主机) {
    ··类型_.行和列_ 行和列 = 获取位置的行和列(文件, 起点); // TODO: GH#18217
    dstring 相对文件名 = 转换到相对路径(文件.文件名, 主机.获取当前目录(), ( 文件名) { return 主机.获取规范文件名(文件名); });
    dstring 输出文本 = ""d;
    输出文本 ~= .格式化颜色和剩余(相对文件名, 前景色转义序列_.青色);
    输出文本 ~= ":"d;
    输出文本 ~= .格式化颜色和剩余(``d ~ (行和列.行 + 1).·to!(dstring)() ~ ``d, 前景色转义序列_.黄色);
    输出文本 ~= ":"d;
    输出文本 ~= .格式化颜色和剩余(``d ~ (行和列.列 + 1).·to!(dstring)() ~ ``d, 前景色转义序列_.黄色);
    return 输出文本;
}
dstring 格式化诊断包括颜色和内容(诊断_[] 诊断组, 格式化诊断主机_ 主机) {
    dstring 输出文本 = ""d;
    foreach (诊断_ 诊断; 诊断组) {
        if (诊断.文件 !is ··null!(Object)) {
            源文件_ 文件 = (cast(源文件_)(诊断.文件));
            long 起点 = 诊断.起点;
            输出文本 ~= .格式化位置(文件, 起点, 主机); // TODO: GH#18217
            输出文本 ~= " - "d;
        }
        输出文本 ~= .格式化颜色和剩余(.获取诊断类别名称(诊断), .获取诊断类别格式(诊断.诊断类别));
        输出文本 ~= .格式化颜色和剩余(` K`d ~ 诊断.代码.·to!(dstring)() ~ `: `d, 前景色转义序列_.灰色);
        输出文本 ~= .格式化诊断消息文本(诊断.消息文本, 主机.获取新行());
        if (诊断.文件 !is ··null!(Object)) {
            输出文本 ~= 主机.获取新行();
            输出文本 ~= .格式化代码跨度((cast(源文件_)(诊断.文件)), 诊断.起点, 诊断.长, ""d, .获取诊断类别格式(诊断.诊断类别), 主机); // TODO: GH#18217
            if (诊断.相关信息组 !is ··null!(诊断相关信息_[])) {
                输出文本 ~= 主机.获取新行();
                foreach (诊断相关信息_ d; 诊断.相关信息组) {
                    源文件_ 文件 = (cast(源文件_)(d.文件));
                    long 起点 = d.起点;
                    long 长度 = d.长;
                    诊断消息_链_ 消息文本 = d.消息文本;
                    if (文件 !is ··null!(源文件_)) {
                        输出文本 ~= 主机.获取新行();
                        输出文本 ~= .半缩进 ~ .格式化位置(文件, 起点, 主机); // TODO: GH#18217
                        输出文本 ~= .格式化代码跨度(文件, 起点, 长度, .缩进, 前景色转义序列_.青色, 主机); // TODO: GH#18217
                    }
                    输出文本 ~= 主机.获取新行();
                    输出文本 ~= .缩进 ~ .格式化诊断消息文本(消息文本, 主机.获取新行());
                }
            }
        }
        输出文本 ~= 主机.获取新行();
    }
    return 输出文本;
}
dstring 格式化代码跨度(源文件_ 文件, long 起点, long 长度, dstring 缩进, 前景色转义序列_ 转议颜色, 格式化诊断主机_ 主机) {
    ··类型_.行和列_ 行和列1 = 获取位置的行和列(文件, 起点);
    long 第一个行 = 行和列1.行;
    long 第一个行的列 = 行和列1.列;
    ··类型_.行和列_ 行和列2 = 获取位置的行和列(文件, 起点 + 长度);
    long 后一行 = 行和列2.行;
    long 后一行的列 = 行和列2.列;
    long 文件的最后一行 = 获取位置的行和列(文件, 文件.内容.length).行;
    bool 有五行以上 = (后一行 - 第一个行) >= 4;
    size_t 隔开的宽度 = ·to!(dstring)(后一行 + 1).length;
    if (有五行以上) {
        隔开的宽度 = 核心.最大(省略号.length, 隔开的宽度);
    }
    dstring 内容 = ""d;
    正则_!(dchar) 替换3 = ·regex(r"\S"d, "g"c);
    正则_!(dchar) 替换4 = ·regex(r"."d, "g"c);
    正则_!(dchar) 替换 = ·regex(r"\s+$"d, "g"c);
    正则_!(dchar) 替换2 = ·regex(r"\t"d, "g"c);
    {
        for (long i = 第一个行; i <= 后一行; ++i) {
            内容 ~= 主机.获取新行();
            // If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
            // so we'll skip ahead to the second-to-last line.
            if (有五行以上 && 第一个行 + 1 < i && i < 后一行 - 1) {
                内容 ~= 缩进 ~ .格式化颜色和剩余(.补充左边(.省略号, 隔开的宽度), .隔开样式系列) ~ .隔开系列 ~ 主机.获取新行();
                i = 后一行 - 1;
            }
            long 行开始 = 计算行和列的位置(获取行开始组(文件), i, 0);
            long 行结束 = i < 文件的最后一行 ? 计算行和列的位置(获取行开始组(文件), i + 1, 0) : 文件.内容.length;
            dstring 行内容 = 文件.内容[行开始 .. 行结束];
            行内容 = 替换.p·替换全部(行内容, ""d); // trim from end
            行内容 = 替换2.p·替换全部(行内容, " "d); // convert tabs to single spaces
            // Output the gutter and the actual contents of the line.
            内容 ~= 缩进 ~ .格式化颜色和剩余(.补充左边(·to!(dstring)(i + 1), 隔开的宽度), .隔开样式系列) ~ .隔开系列;
            内容 ~= 行内容 ~ 主机.获取新行();
            // Output the gutter and the error span 循环 the line using tildes.
            内容 ~= 缩进 ~ .格式化颜色和剩余(.补充左边(""d, 隔开的宽度), .隔开样式系列) ~ .隔开系列;
            内容 ~= 转议颜色.·to!(dstring)();
            if (i == 第一个行) {
                // If we're on the last line, then limit it to the last character of the last line.
                // Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position.
                long 行的最后一个字符 = i == 后一行 ? 后一行的列 : 0;
                内容 ~= 替换3.p·替换全部(行内容[0 .. 第一个行的列], " "d);
                内容 ~= 替换4.p·替换全部(行内容[第一个行的列 .. 行的最后一个字符], "~"d);
            }
            else if (i == 后一行) {
                内容 ~= 替换4.p·替换全部(行内容[0 .. 后一行的列], "~"d);
            }
            else {
                内容 ~= 替换4.p·替换全部(行内容, "~"d);
            }
            内容 ~= .重置转义序列;
        }
    }
    return 内容;
}
T[] 获取且缓存诊断组(T)(源文件_ 源文件 = ··null!(源文件_), 诊断缓存_!(T) 缓存 = ··null!(诊断缓存_!(T)), T[] delegate(源文件_, 取消令牌_) 获取诊断组 = ··null!(T[] delegate(源文件_, 取消令牌_)), 取消令牌_ 取消令牌 = ··null!(取消令牌_)) {
    T[] 缓存的结果;
    if (源文件 !is ··null!(源文件_)) {
        if ((缓存.每个文件 !is null) && (缓存.每个文件.原型 !is ··null!(T[][dstring])) && 缓存.每个文件.具有(源文件.文件名.原型)) {
            缓存的结果 = 缓存.每个文件.获取(源文件.文件名.原型);
        }
    }
    else {
        缓存的结果 = cast(T[])(缓存.全部诊断);
    }
    if (缓存的结果 !is ··null!(T[])) {
        return 缓存的结果;
    }
    T[] 结果 = 获取诊断组(源文件, 取消令牌);
    if (源文件 !is ··null!(源文件_)) {
        if ((缓存.每个文件 is null) || (缓存.每个文件.原型 is ··null!(T[][dstring]))) {
            缓存.每个文件 = 核心.创建词典!(T[])();
        }
        if (结果 !is ··null!(T[])) {
            缓存.每个文件.p·设置(源文件.文件名.原型, 结果);
        }
        else {
            缓存.每个文件.p·设置(源文件.文件名.原型, []);
        }
    }
    else {
        缓存.全部诊断 = cast(诊断_[])(结果);
    }
    if (结果 !is ··null!(T[])) {
        return 结果;
    }
    return ··null!(T[]);
}
T[] 获取诊断助手(T)(源文件_ 源文件 = ··null!(源文件_), T[] delegate(源文件_, 取消令牌_) 获取诊断组 = ··null!(T[] delegate(源文件_, 取消令牌_)), 取消令牌_ 取消令牌 = ··null!(取消令牌_)) {
    if (源文件 !is ··null!(源文件_)) {
        return 获取诊断组(源文件, 取消令牌);
    }
    return .排序且去重诊断组!(T)(核心.扁平化映射组!(源文件_, T)(_当前主机.获取源文件组(), (源文件_ 源文件, size_t _) {
        if (取消令牌 !is ··null!(取消令牌_)) {
            取消令牌.抛出如果取消操作();
        }
        return 获取诊断组(源文件, 取消令牌);
    }));
}
T[] 排序且去重诊断组(T)(T[] 诊断组 = ··null!(T[])) {
    if (诊断组 is ··null!(T[])) {
        return ··null!(T[]);
    }
    return 核心.排序且去重!(T)(诊断组, cast(核心.比较器_!(T))(·fd(&比较诊断)), cast(核心.相等比较器_!(T))(·fd(&比较诊断相等)));
}
诊断_[] 获取语法诊断(源文件_ 源文件 = ··null!(源文件_), 取消令牌_ 取消令牌 = ··null!(取消令牌_)) {
    return .获取诊断助手!(诊断_)(源文件, cast(诊断_[] delegate(源文件_, 取消令牌_))(·fd(&.获取源文件的语法诊断)), 取消令牌);
}
诊断_[] 获取语义诊断(源文件_ 源文件 = ··null!(源文件_), 取消令牌_ 取消令牌 = ··null!(取消令牌_)) {
    return .获取诊断助手!(诊断_)(源文件, cast(诊断_[] delegate(源文件_, 取消令牌_))(·fd(&.获取源文件语义诊断)), 取消令牌);
}
/** @内部 */
诊断_[] 获取源文件的语法诊断(源文件_ 源文件, 取消令牌_ _ = ··null!(取消令牌_)) {
    if (源文件.编译诊断 !is ··null!(诊断_[])) {
        return 源文件.编译诊断;
    }
    return ··null!(诊断_[]);
}
/** @内部 */
诊断_[] 获取源文件语义诊断(源文件_ 源文件, 取消令牌_ 取消令牌 = ··null!(取消令牌_)) {
    return .获取且缓存诊断组!(诊断_)(源文件, ._文件的语义诊断缓存, cast(诊断_[] delegate(源文件_, 取消令牌_))(·fd(&.获取源文件的语义诊断不缓存)), 取消令牌);
}
/** @内部 */
诊断_[] 获取源文件的语义诊断不缓存(源文件_ 源文件, 取消令牌_ 取消令牌 = ··null!(取消令牌_)) {
    return .运行包含取消令牌!(诊断_[])(() {
        检查_ 检查 = .获取生产类型检查();
        诊断_[] 绑定诊断 = 源文件.绑定诊断;
        if (绑定诊断 !is ··null!(诊断_[])) {
            return 绑定诊断;
        }
        诊断_[] 检查诊断 = 检查.获取诊断组(源文件, 取消令牌);
        if (检查诊断 !is ··null!(诊断_[])) {
            return 检查诊断;
        }
        诊断_[] 文件的文件诊断组 = _文件诊断组.获取诊断组(源文件.文件名);
        if ((文件的文件诊断组 !is ··null!(诊断_[])) && 文件的文件诊断组.length > 0) {
            return 文件的文件诊断组;
        }
        诊断_[] 文件的程序诊断组 = _程序诊断组.获取诊断组(源文件.文件名);
        if ((文件的程序诊断组 !is ··null!(诊断_[])) && 文件的程序诊断组.length > 0) {
            return 文件的程序诊断组;
        }
        return ··null!(诊断_[]);
    });
}
诊断_[] 获取选项诊断() {
    诊断_[] 诊断0 = _文件诊断组.获取全局诊断组();
    诊断_[] 诊断1 = _程序诊断组.获取全局诊断组();
    诊断_[] 诊断2 = .getOptionsDiagnosticsOfConfigFile();
    诊断_[] 诊断3 = 创建数组!(诊断_)();
    if (诊断0 !is ··null!(诊断_[])) {
        .追加!(诊断_)(诊断3, 诊断0);
    }
    if (诊断1 !is ··null!(诊断_[])) {
        .追加!(诊断_)(诊断3, 诊断1);
    }
    if (诊断2 !is ··null!(诊断_[])) {
        .追加!(诊断_)(诊断3, 诊断2);
    }
    if (诊断3.length > 0) {
        return ··null!(诊断_[]);
    }
    return .排序且去重诊断组!(诊断_)(诊断3);
}
诊断_[] getOptionsDiagnosticsOfConfigFile() {
    if ((_options.配置源文件 !is ··null!(源文件_)) && (_options.配置源文件.编译诊断 !is ··null!(诊断_[]))) {
        return _options.配置源文件.编译诊断;
    }
    return ··null!(诊断_[]);
}
诊断_[] 获取全局诊断组() {
    if ((._rootNames !is ··null!(dstring[])) && _rootNames.length > 0) {
        诊断_[] 结果 = .获取生产类型检查().获取全局诊断组();
        if (结果 !is ··null!(诊断_[])) {
            return .排序且去重诊断组!(诊断_)(结果[0 .. $]);
        }
    }
    return ··null!(诊断_[]);
}
源文件_ getSourceFile(dstring fileName, 解析的模块名称_ 解析的 = ··null!(解析的模块名称_), void delegate(dstring) onError = ··null!(void delegate(dstring)), bool 应创建新文件 = ··null!(bool)) {
    return .getSourceFileByPath(.toPath(fileName));
}
源文件_ getSourceFileByPath(路径_ path) {
    if (filesByName.具有(path)) {
        return filesByName.获取(path);
    }
    return ··null!(源文件_);
}
输出结果_ emitWorker(程序主机_ program, 源文件_ 源文件 = ··null!(源文件_), 写文件回调_ 写文件回调 = ··null!(写文件回调_), 取消令牌_ 取消令牌 = ··null!(取消令牌_), 定制转换组_ 定制转换组 = ··null!(定制转换组_)) {
    bool 是转换主机 = program.是转换主机;
    if (!是转换主机) {
        输出结果_ 错误结果 = new 输出结果_(false, 核心.创建数组!(诊断_)());
        诊断_[] 选项诊断 = program.获取选项诊断();
        if ((选项诊断 !is ··null!(诊断_[])) && 选项诊断.length > 0) {
            错误结果.diagnostics = 选项诊断;
            return 错误结果;
        }
        诊断_[] 全局诊断 = program.获取全局诊断组();
        if ((全局诊断 !is ··null!(诊断_[])) && 全局诊断.length > 0) {
            错误结果.diagnostics = 全局诊断;
            return 错误结果;
        }
        诊断_[] 语法诊断 = program.获取语法诊断(源文件, 取消令牌);
        if ((语法诊断 !is ··null!(诊断_[])) && 语法诊断.length > 0) {
            错误结果.diagnostics = 语法诊断;
            return 错误结果;
        }
        诊断_[] 语义诊断 = program.获取语义诊断(源文件, 取消令牌);
        if ((语义诊断 !is ··null!(诊断_[])) && 语义诊断.length > 0) {
            错误结果.diagnostics = 语义诊断;
            return 错误结果;
        }
    }
    检查_ 检查 = .获取生产类型检查(!是转换主机);
    ··类型_.输出解析器_ emitResolver = 检查.获取输出解析器(源文件, 取消令牌);
    输出结果_ emitResult = emitFiles(emitResolver, cast(写文件回调_)(写文件回调), 源文件, getTransformers(program.获取前端()));
    return emitResult;
}
路径_ toPath(dstring 文件名) {
    return new 路径_(转为路径(文件名, 当前编译主机.获取当前目录(), cast(dstring delegate(dstring))(&当前编译主机.getCanonicalFileName)));
}
// Get source file from normalized fileName
源文件_ findSourceFile(dstring fileName, 路径_ path, bool isDefaultLib, 引用文件_ refFile = ··null!(引用文件_), 解析的模块名称_ 解析的 = ··null!(解析的模块名称_)) {
    dstring originalFileName = fileName;
    if (filesByName.具有(path)) {
        源文件_ file = filesByName.获取(path);
        // try to check 如果 we've already seen this file but with a different casing in path
        // NOTE: this only makes sense for case-insensitive file systems, and only on files which are not redirected
        if (file !is ··null!(源文件_)) {
            dstring inputName = fileName;
            路径_ checkedName = file.文件名;
            // Check 如果 it differs only in drive letters its ok to ignore that error:
            dstring checkedAbsolutePath = 获取正规化绝对路径包括根(checkedName, 当前编译主机.获取当前目录());
            dstring inputAbsolutePath = 获取正规化绝对路径包括根(inputName, 当前编译主机.获取当前目录());
            if (checkedAbsolutePath != inputAbsolutePath) {
                .reportFileNamesDifferOnlyInCasingError(inputName, checkedName, refFile);
            }
        }
        // If the file was previously found via a node_modules search, but is now being processed as a root file,
        // then everything it sucks in may also be marked incorrectly, and needs to be checked again.
        if ((file !is ··null!(源文件_)) && sourceFilesFoundSearchingNodeModules.具有(file.文件名) && .currentNodeModulesDepth == 0) {
            sourceFilesFoundSearchingNodeModules.p·设置(file.文件名, false);
            modulesWithElidedImports.p·设置(file.文件名, false);
            .processImportedModules(file);
        }
        // See 如果 we need to reprocess the imports due to prior skipped imports
        else 
        // See 如果 we need to reprocess the imports due to prior skipped imports
        if ((file !is ··null!(源文件_)) && modulesWithElidedImports.具有(file.文件名)) {
            if (.currentNodeModulesDepth < .maxNodeModuleJsDepth) {
                modulesWithElidedImports.p·设置(file.文件名, false);
                .processImportedModules(file);
            }
        }
        .addFileToRefFileMap(file, refFile);
        if (file !is ··null!(源文件_)) {
            return file;
        }
        return ··null!(源文件_);
    }
    路径_ redirectedPath;
    // We haven't looked for this file, do so now and cache result
    源文件_ file = 当前编译主机.getSourceFile(fileName, 解析的, (dstring hostErrorMessage) { return _文件诊断组.add(.创建引用文件诊断(refFile, 诊断集_.Cannot_read_file_0_Colon_1, [fileName,
        hostErrorMessage])); }, .shouldCreateNewSourceFile);
    .addFileToFilesByName(file, path, redirectedPath);
    if (file !is ··null!(源文件_)) {
        sourceFilesFoundSearchingNodeModules.p·设置(path, .currentNodeModulesDepth > 0);
        file.路径 = path;
        file.resolvedPath = .toPath(fileName);
        file.originalFileName = originalFileName;
        if (当前编译主机.useCaseSensitiveFileNames()) {
            dstring pathLowerCase = 核心.文本转小写(path);
            // for case-sensitive file systems check 如果 we've already seen some file with similar filename ignoring case
            if (filesByNameIgnoreCase.具有(pathLowerCase)) {
                源文件_ existingFile = filesByNameIgnoreCase.获取(pathLowerCase);
                .reportFileNamesDifferOnlyInCasingError(fileName, existingFile.文件名, refFile);
            }
            else {
                filesByNameIgnoreCase.p·设置(pathLowerCase, file);
            }
        }
        // always process imported modules to record module name resolutions
        .processImportedModules(file);
        if (isDefaultLib) {
            file.是默认支持库 = true;
            .追加!(源文件_)(.processingDefaultLibFiles, file);
        }
        else {
            .追加!(源文件_)(.processingOtherFiles, file);
        }
    }
    .addFileToRefFileMap(file, refFile);
    return file;
}
void addFileToRefFileMap(源文件_ file = ··null!(源文件_), 引用文件_ refFile = ··null!(引用文件_)) {
    if ((file !is ··null!(源文件_)) && (refFile !is ··null!(引用文件_))) {
        if (refFileMap.具有(file.文件名)) {
            .追加!(引用文件_)(refFileMap.获取(file.文件名), refFile);
        }
        else {
            refFileMap.p·设置(file.文件名, [refFile]);
        }
    }
}
void addFileToFilesByName(源文件_ file = ··null!(源文件_), 路径_ path = ··null!(路径_), 路径_ redirectedPath = ··null!(路径_)) {
    if ((redirectedPath !is null) && (redirectedPath.原型 !is ··null!(dstring))) {
        filesByName.p·设置(redirectedPath, file);
        filesByName.p·设置(path, file);
    }
    else {
        filesByName.p·设置(path, file);
    }
}
void reportFileNamesDifferOnlyInCasingError(dstring fileName, dstring existingFileName, 引用文件_ refFile = ··null!(引用文件_)) {
    _文件诊断组.add(.创建引用文件诊断(refFile, 诊断集_.File_name_0_differs_from_already_included_file_name_1_only_in_casing, [fileName,
        existingFileName]));
}
诊断_ 创建引用文件诊断(引用文件_ refFile = ··null!(引用文件_), 诊断消息_ message = ··null!(诊断消息_), dstring[] args = ··null!(dstring[])) {
    args = args.dup;
    if (refFile is ··null!(引用文件_)) {
        return 创建编译诊断(message, args);
    }
    else {
        return 创建文件诊断消息(refFile.file, refFile.开始, refFile.结束 - refFile.开始, message, args);
    }
}
void processImportedModules(源文件_ file) {
    void delegate(源文件_) 收集函数 = 当前编译主机.获取前端().获取引用模块收集函数(.当前编译主机);
    收集函数(file);
    if ((file.导入组 !is ··null!(模块导入信息_[])) && file.导入组.length > 0) {
        dstring[] 模块名称组 = .收集模块名(file.导入组);
        解析的模块名称_[] 解析的模块名称 = 当前编译主机.获取前端().加载模块(模块名称组, file.文件名);
        {
            for (int i = 0; i < 解析的模块名称.length; ++i) {
                解析的模块名称_ 解析的 = 解析的模块名称[i];
                .设置解析的模块(file, 解析的.模块名称, 解析的);
                dstring[] resolvedFileNames = 解析的.全部源码文件名;
                if (resolvedFileNames.length > 0) {
                    {
                        for (int ii = 0; ii < resolvedFileNames.length; ++ii) {
                            dstring resolvedFileName = resolvedFileNames[ii];
                            路径_ path = .toPath(resolvedFileName);
                            long pos = 跳过杂项(file.内容, file.导入组[i].开始);
                            引用文件_ 引用文件 = new 引用文件_(i, file, pos, file.导入组[i].结束);
                            .findSourceFile(resolvedFileName, path, /*isDefaultLib*/ false, 引用文件, 解析的);
                        }
                    }
                }
            }
        }
    }
}
dstring[] 收集模块名(模块导入信息_[] 导入组) {
    return 核心.映射!(模块导入信息_, dstring)(导入组, (模块导入信息_ v, size_t _) { return v.模块名; });
}
void 设置解析的模块(源文件_ file, dstring 模块名称文本, 解析的模块名称_ 解析的模块名) {
    if ((file.resolvedModules is null) || (file.resolvedModules.原型 is ··null!(解析的模块名称_[dstring]))) {
        file.resolvedModules = 核心.创建词典!(解析的模块名称_)();
    }
    dstring 正规模块名 = 正规化路径(模块名称文本);
    file.resolvedModules.p·设置(正规模块名, 解析的模块名);
}
void processRootFile(dstring fileName, bool isDefaultLib) {
    .processSourceFile(正规化路径(fileName), isDefaultLib);
}
/** This has side effects through `findSourceFile`. */
void processSourceFile(dstring fileName, bool isDefaultLib, 引用文件_ refFile = ··null!(引用文件_)) {
    .getSourceFileFromReferenceWorker(fileName, ( fileName) { return .findSourceFile(fileName, .toPath(fileName), isDefaultLib, refFile); }, // TODO: GH#18217
    (诊断消息_ diagnostic, dstring[] args) { return _文件诊断组.add(.创建引用文件诊断(refFile, diagnostic, args)); }, refFile);
}
源文件_ getSourceFileFromReferenceWorker(dstring fileName, 源文件_ delegate(dstring) getSourceFile, void delegate(诊断消息_, dstring[]) fail = ··null!(void delegate(诊断消息_, dstring[])), 引用文件_ refFile = ··null!(引用文件_)) {
    if (有扩展名(fileName)) {
        dstring[] supportedExtensionsWithJsonIfResolveJsonModule = 当前编译主机.前端.获取支持的扩展();
        if (!循环每个!(dstring, bool)(supportedExtensionsWithJsonIfResolveJsonModule, (dstring extension, size_t _) { return 文件扩展名是(当前编译主机.getCanonicalFileName(fileName), extension); })) {
            if (fail !is ··null!(void delegate(诊断消息_, dstring[]))) {
                fail(诊断集_.File_0_has_unsupported_extension_The_only_supported_extensions_are_1, [fileName, "'"d ~ 核心.连接(supportedExtensionsWithJsonIfResolveJsonModule, "', '"d) ~ "'"d]);
            }
            return ··null!(源文件_);
        }
        源文件_ sourceFile = getSourceFile(fileName);
        if (fail !is ··null!(void delegate(诊断消息_, dstring[]))) {
            if (sourceFile is ··null!(源文件_)) {
                fail(诊断集_.没发现文件_0, [fileName]);
            }
            else if ((refFile !is ··null!(引用文件_)) && 当前编译主机.getCanonicalFileName(fileName) == 当前编译主机.getCanonicalFileName(refFile.file.文件名)) {
                fail(诊断集_.A_file_cannot_have_a_reference_to_itself, [""d]);
            }
        }
        return sourceFile;
    }
    else {
        if (fail !is ··null!(void delegate(诊断消息_, dstring[]))) {
            fail(诊断集_.没发现文件_0, [fileName]);
            return ··null!(源文件_);
        }
    }
    return ··null!(源文件_);
}
